﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Threading;
using System.Diagnostics;

namespace Crayons.Components
{
    public class Document
    {
        public enum ToolMode
        {
            Move,
            ZoomIn,
            ZoomOut,
            PickSelection,
            EditSelection,
            GetAlphaColor,
        }

        protected const int Distance = 8;
        protected CanvasPanel mCanvas;
        protected Graphics mGraph;
        protected Bitmap mContent;
        protected Surface mSurface;
        protected ToolMode mDocMode;
       
        protected Thread mThread;
        protected Stopwatch mWatch;

        protected Point posRelativeToMouse;
        protected bool mFillDocWithContent;

        protected LayerCollection mLayers;

        protected SelectionCollection m_selections;

        protected Point m_relative;

        public Size ContentSize
        {
            get { return mSurface.ContentSize; }
            set { mSurface.ContentSize = value; }
        }

        public Bitmap ContentImage
        {
            get { return mContent; }
        }

        public Document(Control canvasPanel)
        {
            if (!(canvasPanel is CanvasPanel))
                return;

            mDocMode = ToolMode.EditSelection;
            mCanvas = (CanvasPanel)canvasPanel;
            mFillDocWithContent = false;
            mContent = new Bitmap(canvasPanel.Width, canvasPanel.Height);
            mGraph = Graphics.FromImage(mContent);
            mGraph.CompositingMode = CompositingMode.SourceCopy;
            mGraph.CompositingQuality = CompositingQuality.AssumeLinear;
            mGraph.SmoothingMode = SmoothingMode.None;
            mGraph.InterpolationMode = InterpolationMode.NearestNeighbor;
            mGraph.PixelOffsetMode = PixelOffsetMode.HighSpeed;

            mSurface = new Surface();
            mSurface.Position = new Point(Distance, Distance);
            
            m_selections = new SelectionCollection(mSurface);
            m_selections.MultiSelection = false;

            mLayers = new LayerCollection(mSurface);
            mThread = new Thread(new ThreadStart(this.RenderAll));
            
         }

        public void OnLoad()
        {
            mWatch = Stopwatch.StartNew();
            mThread.Start();
        }

        public void OnSizeChanged(int w, int h)
        {
            mContent = new Bitmap(w, h);
            mGraph = Graphics.FromImage(mContent);
            mLayers.ViewSize = new Size(mSurface.ContentSize.Width, mSurface.ContentSize.Height);
        }

        public void OnMouseDown(MouseEventArgs e)
        {
            if(mDocMode == ToolMode.Move)
            {   
                if (e.Button == MouseButtons.Left)
                {
                    m_relative = mSurface.ToSurfaceLocalCoord(e.Location);
                }
            }
            else if (mDocMode == ToolMode.EditSelection)
            {
                if (e.Button == MouseButtons.Left)
                {
                    if (m_selections.SelectedSelections.Count == 1)
                    {
                        Selection.HandleType ht = m_selections.SelectedSelections[0].PickHandle(e.Location);
                        if (ht != Selection.HandleType.None)
                        {
                            m_selections.SelectedSelections[0].GrabbedHandle = ht;
                            return;
                        }
                    }

                    Selection s = new Selection(mSurface);
                    s.Selected = true;
                    s.GrabbedHandle = Selection.HandleType.TopLeft;
                    s.GrabbedHandlePosition = mSurface.ToSurfaceLocalCoord(e.Location);
                    s.GrabbedHandle = Selection.HandleType.BottomRight;
                    s.GrabbedHandlePosition = mSurface.ToSurfaceLocalCoord(e.Location);
                    m_selections.AddSelection(s);
                }
            }
            else if (mDocMode == ToolMode.PickSelection)
            {
                if (e.Button == MouseButtons.Left)
                {
                    Point mp = mSurface.ToSurfaceLocalCoord(e.Location);
                    for(int i = 0; i < m_selections.Count; i++)
                    {
                        if (m_selections[i].BoundingRect.Contains(mp))
                        {
                            m_selections[i].Selected = true;
                            m_relative = new Point(mp.X - m_selections[i].BoundingRect.X, mp.Y - m_selections[i].BoundingRect.Y);
                            return;
                        }
                        else
                        {
                            m_selections[i].Selected = false;
                        }
                    }
                }
            }
            else if (mDocMode == ToolMode.GetAlphaColor)
            {
                if (e.Button == MouseButtons.Left)
                {
                    if (mLayers.Count > 0)
                    {
						// temporarily get the first layer as main layer
                        mLayers[0].AlphaColor = mLayers[0].GetPixel(e.X, e.Y);
                        mLayers[0].RefreshImage();
                    }
                }
            }
        }

        public void OnMouseUp(MouseEventArgs e)
        {
            if (m_selections.SelectedSelections.Count == 1)
            {
                m_selections.SelectedSelections[0].GrabbedHandle = Selection.HandleType.None;
            }
            if (mDocMode == ToolMode.EditSelection)
            {
                if (e.Button == MouseButtons.Left)
                {
                    m_selections.ClearZero();
                }
            }
        }

        public void OnMouseMove(MouseEventArgs e)
        {
            if(mDocMode == ToolMode.Move)
            {
                if (e.Button == MouseButtons.Left)
                {
                    mSurface.ViewLocation = new Point(m_relative.X - (int)((e.X - mSurface.Position.X) / mSurface.ScaleRate),
                        m_relative.Y - (int)((e.Y - mSurface.Position.Y) / mSurface.ScaleRate));
                }
            }
            else if(mDocMode == ToolMode.PickSelection)
            {
                if (e.Button == MouseButtons.None)
                {
                    Point mp = mSurface.ToSurfaceLocalCoord(e.Location);
                    for (int i = 0; i < m_selections.SelectedSelections.Count; i++)
                        if (m_selections.SelectedSelections[i].BoundingRect.Contains(mp))
                        {
                            mCanvas.Cursor = Cursors.SizeAll;
                            return;
                        }
                    mCanvas.Cursor = Cursors.Arrow;
                }

                if (e.Button == MouseButtons.Left)
                {
                    Point mp = mSurface.ToSurfaceLocalCoord(e.Location);
                    
                    for (int i = 0; i < 1/*m_selections.SelectedSelections.Count*/; i++)
                    {
                        Rectangle br = m_selections.SelectedSelections[i].BoundingRect;
                        m_selections.SelectedSelections[i].BoundingRect = new Rectangle(mp.X - m_relative.X, mp.Y - m_relative.Y, br.Width, br.Height);
                    }
                }
            }
            else if (mDocMode == ToolMode.EditSelection)
            {
                if (e.Button == MouseButtons.Left)
                {
                    if (m_selections.SelectedSelections.Count > 0)
                    {
                        m_selections.SelectedSelections[0].GrabbedHandlePosition = mSurface.ToSurfaceLocalCoord(e.Location);
                    }
                }

                if (e.Button == MouseButtons.None)
                {
                    if (m_selections.SelectedSelections.Count == 1)
                    {
                        switch (m_selections.SelectedSelections[0].PickHandle(e.Location))
                        {
                            case Selection.HandleType.Left:
                            case Selection.HandleType.Right:
                                mCanvas.Cursor = Cursors.SizeWE;
                                break;

                            case Selection.HandleType.Top:
                            case Selection.HandleType.Bottom:
                                mCanvas.Cursor = Cursors.SizeNS;
                                break;

                            case Selection.HandleType.TopLeft:
                            case Selection.HandleType.BottomRight:
                                mCanvas.Cursor = Cursors.SizeNWSE;
                                break;

                            case Selection.HandleType.BottomLeft:
                            case Selection.HandleType.TopRight:
                                mCanvas.Cursor = Cursors.SizeNESW;
                                break;

                            default:
                                mCanvas.Cursor = Cursors.Arrow;
                                break;
                        }
                    }
                }
            }
        }

        bool xxx = true;
        public void OnKeyUp(KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Z)
            {
                mSurface.ScaleRate += 1.0f;
            }
            if (e.KeyCode == Keys.A)
            {
                mSurface.ScaleRate -= 1.0f;
            }

            if (e.KeyCode == Keys.M)
            {
                mDocMode = ToolMode.Move;
                mCanvas.Cursor = Cursors.Hand;
            }

            if (e.KeyCode == Keys.E)
            {
                mDocMode = ToolMode.EditSelection;
            }

            if (e.KeyCode == Keys.X)
            {
                mDocMode = ToolMode.PickSelection;
            }

            if (e.KeyCode == Keys.G)
            {
                mDocMode = ToolMode.GetAlphaColor;
            }

            if (e.KeyCode != Keys.Space)
                return;
            if (xxx)
            {
                mLayers.AddLayer("C:\\Users\\an.vuongngocduy\\Downloads\\bass10sheet.png");
                //mLayers.AddLayer("D:\\cloud.png");
                xxx = false;
            }
            /*else
            {
                mSurface.ContentSize = new Size(mSurface.ContentSize.Width * 2, mSurface.ContentSize.Height * 2);
                mLayers.ZoomLevel = 2;
            }*/
        }

        public void RenderAll()
        {
            Int32 msPerFrame = 1000 / 24;
            while (!mainForm.isEnd) //TODO: add a condition to stop this infinite loop
            {
                Int32 curStop = (Int32) mWatch.ElapsedMilliseconds;
                if (msPerFrame > curStop)
                {
                    Thread.Sleep(msPerFrame - curStop);
                    mWatch.Reset();
                }
                
                mGraph.Clear(Color.White);
                mSurface.ClearWithCheckerboardPattern();                

                mLayers.Prepare();
                if (mLayers.ViewBitmap != null && mLayers.Count > 0)
                {
                    mSurface.DrawBitmap(mLayers.ViewBitmap, 0, 0);
                }

                mSurface.DrawSelections(m_selections);

                mGraph.DrawImageUnscaled(mSurface.ContentImage, mSurface.Position.X, mSurface.Position.Y,
                    mSurface.ContentSize.Width + Surface.kShadowDistance, mSurface.ContentSize.Height + Surface.kShadowDistance);

                //m_selections.Flush(mGraph);

                mCanvas.Invalidate();
            }
        }
    }
}
